import { computed, ref } from 'vue'
import type { RouteRecordRaw } from 'vue-router'
import { defineStore } from 'pinia'
import { useBoolean } from '@sa/hooks'
import type { CustomRoute, ElegantConstRoute, LastLevelRouteKey, RouteKey, RouteMap } from '@elegant-router/types'
import { router } from '@/router'
import { SetupStoreId } from '@/enum'
import { ROOT_ROUTE, createRoutes, getAuthVueRoutes } from '@/router/routes'
import { getRouteName, getRoutePath } from '@/router/elegant/transform'
import { fetchGetUserRoutes, fetchIsRouteExist } from '@/service/api'
import { useAppStore } from '../app'
import { useAuthStore } from '../auth'
import { useTabStore } from '../tab'
import {
  filterAuthRoutesByRoles,
  getBreadcrumbsByRoute,
  getCacheRouteNames,
  getGlobalMenusByAuthRoutes,
  getSelectedMenuKeyPathByKey,
  isRouteExistByRouteName,
  sortRoutesByOrder,
  updateLocaleOfGlobalMenus
} from './shared'

export const useRouteStore = defineStore(SetupStoreId.Route, () => {
  const appStore = useAppStore()
  const authStore = useAuthStore()
  const tabStore = useTabStore()
  const { bool: isInitAuthRoute, setBool: setIsInitAuthRoute } = useBoolean()
  const removeRouteFns: (() => void)[] = []

  /**
   * Auth route mode
   *
   * It recommends to use static mode in the development environment, and use dynamic mode in the production
   * environment, if you use static mode in development environment, the auth routes will be auto generated by plugin
   * "@elegant-router/vue"
   */
  const authRouteMode = ref(import.meta.env.VITE_AUTH_ROUTE_MODE)
  if (process.env.NODE_ENV === 'development') {
  }
  /** Home route key */
  const routeHome = ref(import.meta.env.VITE_ROUTE_HOME)

  /**
   * Set route home
   *
   * @param routeKey Route key
   */
  function setRouteHome(routeKey: LastLevelRouteKey) {
    routeHome.value = routeKey
  }

  /** Global menus */
  const menus = ref<App.Global.Menu[]>([])

  /** Get global menus */
  function getGlobalMenus(routes: ElegantConstRoute[]) {
    menus.value = getGlobalMenusByAuthRoutes(routes)
  }

  /** Update global menus by locale */
  function updateGlobalMenusByLocale() {
    menus.value = updateLocaleOfGlobalMenus(menus.value)
  }

  /** Cache routes */
  const cacheRoutes = ref<RouteKey[]>([])

  /**
   * Get cache routes
   *
   * @param routes Vue routes
   */
  function getCacheRoutes(routes: RouteRecordRaw[]) {
    const { constantVueRoutes } = createRoutes()

    cacheRoutes.value = getCacheRouteNames([...constantVueRoutes, ...routes])
  }

  /**
   * Add cache routes
   *
   * @param routeKey
   */
  function addCacheRoutes(routeKey: RouteKey) {
    if (cacheRoutes.value.includes(routeKey)) return

    cacheRoutes.value.push(routeKey)
  }

  /**
   * Remove cache routes
   *
   * @param routeKey
   */
  function removeCacheRoutes(routeKey: RouteKey) {
    const index = cacheRoutes.value.findIndex(item => item === routeKey)

    if (index === -1) return

    cacheRoutes.value.splice(index, 1)
  }

  /**
   * Re-cache routes by route key
   *
   * @param routeKey
   */
  async function reCacheRoutesByKey(routeKey: RouteKey) {
    removeCacheRoutes(routeKey)

    await appStore.reloadPage()

    addCacheRoutes(routeKey)
  }

  /**
   * Re-cache routes by route keys
   *
   * @param routeKeys
   */
  async function reCacheRoutesByKeys(routeKeys: RouteKey[]) {
    for await (const key of routeKeys) {
      await reCacheRoutesByKey(key)
    }
  }

  /** Global breadcrumbs */
  const breadcrumbs = computed(() => getBreadcrumbsByRoute(router.currentRoute.value, menus.value))

  /** Reset store */
  async function resetStore() {
    const routeStore = useRouteStore()

    routeStore.$reset()

    resetVueRoutes()
  }

  /** Reset vue routes */
  function resetVueRoutes() {
    removeRouteFns.forEach(fn => fn())
    removeRouteFns.length = 0
  }

  /** Init auth route */
  async function initAuthRoute(): Promise<boolean> {
    let success = false
    if (authRouteMode.value === 'static') {
      success = await initStaticAuthRoute()
    } else {
      success = await initDynamicAuthRoute()
    }

    if (success) {
      tabStore.initHomeTab()
    }

    return success
  }

  /** Init static auth route */
  async function initStaticAuthRoute(): Promise<boolean> {
    const { authRoutes } = createRoutes()

    const filteredAuthRoutes = filterAuthRoutesByRoles(authRoutes, authStore?.userInfo?.roles as string[])

    handleAuthRoutes(filteredAuthRoutes)

    setIsInitAuthRoute(true)

    return true // Indicate success
  }

  /** Init dynamic auth route */
  async function initDynamicAuthRoute(): Promise<boolean> {
    const { data, error } = await fetchGetUserRoutes()

    if (!error) {
      const routes = data?.list

      handleAuthRoutes(routes)

      setRouteHome('home')

      handleUpdateRootRouteRedirect('home')

      setIsInitAuthRoute(true)

      return true // Indicate success
    }

    // Optionally handle the error, e.g., show a notification
    // window.$message?.error('Failed to fetch user routes.');

    return false // Indicate failure
  }

  /**
   * Handle routes
   *
   * @param routes Auth routes
   */
  function handleAuthRoutes(routes: ElegantConstRoute[]) {
    const sortRoutes = sortRoutesByOrder(routes)

    const vueRoutes = getAuthVueRoutes(sortRoutes)

    addRoutesToVueRouter(vueRoutes)

    getGlobalMenus(sortRoutes)

    getCacheRoutes(vueRoutes)
  }

  /**
   * Add routes to vue router
   *
   * @param routes Vue routes
   */
  function addRoutesToVueRouter(routes: RouteRecordRaw[]) {
    routes.forEach(route => {
      const removeFn = router.addRoute(route)
      addRemoveRouteFn(removeFn)
    })
  }

  /**
   * Add remove route fn
   *
   * @param fn
   */
  function addRemoveRouteFn(fn: () => void) {
    removeRouteFns.push(fn)
  }

  /**
   * Update root route redirect when auth route mode is dynamic
   *
   * @param redirectKey Redirect route key
   */
  function handleUpdateRootRouteRedirect(redirectKey: LastLevelRouteKey) {
    const redirect = getRoutePath(redirectKey)

    if (redirect) {
      const rootRoute: CustomRoute = { ...ROOT_ROUTE, redirect }

      router.removeRoute(rootRoute.name)

      const [rootVueRoute] = getAuthVueRoutes([rootRoute])

      router.addRoute(rootVueRoute)
    }
  }

  /**
   * Get is auth route exist
   *
   * @param routePath Route path
   */
  async function getIsAuthRouteExist(routePath: RouteMap[RouteKey]) {
    const routeName = getRouteName(routePath)

    if (!routeName) {
      return false
    }

    if (authRouteMode.value === 'static') {
      const { authRoutes } = createRoutes()

      return isRouteExistByRouteName(routeName, authRoutes)
    }

    const data = fetchIsRouteExist(routeName)

    return data
  }

  /**
   * Get selected menu key path
   *
   * @param selectedKey Selected menu key
   */
  function getSelectedMenuKeyPath(selectedKey: string) {
    return getSelectedMenuKeyPathByKey(selectedKey, menus.value)
  }

  return {
    resetStore,
    routeHome,
    menus,
    updateGlobalMenusByLocale,
    cacheRoutes,
    reCacheRoutesByKey,
    reCacheRoutesByKeys,
    breadcrumbs,
    initAuthRoute,
    isInitAuthRoute,
    setIsInitAuthRoute,
    getIsAuthRouteExist,
    getSelectedMenuKeyPath
  }
})
